/*
 * Partida.cpp
 *
 *  Created on: Nov 2, 2013
 *      Author: javier
 */

#include "Partida.h"

using Golosinas::Pieza;
using Golosinas::Button;
using Golosinas::GoldenStar;
using Golosinas::MiniBar;

const unsigned int NFILAS = 8;
const unsigned int NCOLS = 8;


Partida::Partida() {
	JsonLevel::setLevel("nivel.json");
	miTablero.setColumnas(JsonLevel::getColumnas());
	miTablero.setFilas(JsonLevel::getFilas());
	//llenarTablero();
	completarTablero();
	puntosJugada = 0;
}

Partida::~Partida() {}


void Partida::imprimirTablero(){
	miTablero.imprimir();
}

int Partida::getVerMatches(unsigned int nFila, unsigned int nCol){
	unsigned int j = 1;
	unsigned int matchesVer = 0;
	CeldaBase* celda = miTablero.getCelda(nFila,nCol);
	Pieza::orientacion o;
	std::vector<Posicion> aEliminarTmp;
	bool foundBarritaH = (celda->getGolosina()->getOrientacion() == Pieza::horizontal);
	if (foundBarritaH){
		eliminarColumnaCompleta(nFila, nCol, &aEliminarTmp);
	}
	//chequeo los matches hacia abajo
	//Operador == sobrecargado para las celdas
	while( ((nFila+j) < miTablero.getFilas()) && (*celda == *miTablero.getCelda(nFila+j,nCol))){
		if (!foundBarritaH){
			aEliminarTmp.push_back(Posicion(nFila+j, nCol));
		}
		o = miTablero.getCelda(nFila+j,nCol)->getGolosina()->getOrientacion();
		if (o == Pieza::vertical){
			eliminarFilaCompleta(nFila+j, nCol, &aEliminarTmp);
		}else if (o == Pieza::horizontal){
			aEliminarTmp.clear();
			eliminarColumnaCompleta(nFila, nCol, &aEliminarTmp);
			foundBarritaH = true;
		}
		j++;
		matchesVer++;
	}
	j=1;
	//chequeo los matches hacia arriba
	while((nFila >= j) &&(*celda == *miTablero.getCelda(nFila-j,nCol))){
		if (!foundBarritaH){
			aEliminarTmp.push_back(Posicion(nFila-j, nCol));
		}
		o = miTablero.getCelda(nFila-j,nCol)->getGolosina()->getOrientacion();
		if (o == Pieza::vertical){
			eliminarFilaCompleta(nFila-j, nCol, &aEliminarTmp);
		}else if (o == Pieza::horizontal){
			aEliminarTmp.clear();
			eliminarColumnaCompleta(nFila, nCol, &aEliminarTmp);
			foundBarritaH = true;
		}
		j++;
		matchesVer++;
	}
	if(celda->getGolosina()->getOrientacion() == Pieza::vertical){
		eliminarFilaCompleta(nFila, nCol, &aEliminarTmp);
	}
	//Si hay matches suficientes, los agrego
	if(matchesVer > 1){
		for (std::vector<Posicion>::iterator it = aEliminarTmp.begin();
						it != aEliminarTmp.end(); ++it) {
			if (miTablero.getCelda((*it).getX(), (*it).getY())->getGolosina() != NULL){
				miTablero.getCelda((*it).getX(), (*it).getY())->getGolosina()->setPuntos((matchesVer -1)*10);
			}
		}
		aEliminar.insert(aEliminar.end(), aEliminarTmp.begin(), aEliminarTmp.end());
	}
	return matchesVer;
}

int Partida::getHorMatches(unsigned int nFila, unsigned int nCol){
	unsigned int i = 1;
	unsigned int matchesHor = 0;
	std::vector<Posicion> aEliminarTmp;
	CeldaBase* celda = miTablero.getCelda(nFila,nCol);
	Pieza::orientacion o;
	bool foundBarritaV = (celda->getGolosina()->getOrientacion() == Pieza::vertical);
	if (foundBarritaV){
		eliminarFilaCompleta(nFila, nCol, &aEliminarTmp);
	}
	//chequeo los matches a derecha
	while( ((nCol + i )< miTablero.getColumnas()) && (*celda == *miTablero.getCelda(nFila,nCol+i))){
			if (!foundBarritaV){
				aEliminarTmp.push_back(Posicion(nFila, nCol+i));
			}
			o = miTablero.getCelda(nFila,nCol+i)->getGolosina()->getOrientacion();
			if (o == Pieza::vertical){
				aEliminarTmp.clear();
				eliminarFilaCompleta(nFila, nCol, &aEliminarTmp);
				foundBarritaV = true;
			}else if (Pieza::horizontal){
				eliminarColumnaCompleta(nFila, nCol+i, &aEliminarTmp);
			}
			i++;
			matchesHor++;
	}
	i=1;
	//chequeo los matches a izquierda
	while((nCol >= i) && (*celda == *miTablero.getCelda(nFila,nCol-i))){
			if (!foundBarritaV){
				aEliminarTmp.push_back(Posicion(nFila, nCol-i));
			};
			o = miTablero.getCelda(nFila,nCol-i)->getGolosina()->getOrientacion();
			if (o == Pieza::vertical){
				aEliminarTmp.clear();
				eliminarFilaCompleta(nFila, nCol, &aEliminarTmp);
				foundBarritaV = true;
			}else if (Pieza::horizontal){
				eliminarColumnaCompleta(nFila, nCol-i, &aEliminarTmp);
			}
			i++;
			matchesHor++;

	}
	if(celda->getGolosina()->getOrientacion() == Pieza::horizontal){
		eliminarColumnaCompleta(nFila, nCol, &aEliminarTmp);
	}
	//Si hay matches suficientes, los agrego
	if(matchesHor > 1){
		for (std::vector<Posicion>::iterator it = aEliminarTmp.begin();
						it != aEliminarTmp.end(); ++it) {
			if (miTablero.getCelda((*it).getX(), (*it).getY())->getGolosina() != NULL){
				miTablero.getCelda((*it).getX(), (*it).getY())->getGolosina()->setPuntos((matchesHor-1)*10);
			}
		}
		aEliminar.insert(aEliminar.end(), aEliminarTmp.begin(), aEliminarTmp.end());
	}
	return (matchesHor);
}

std::vector<Posicion> Partida::getPosicionesAEliminar(){
	return aEliminar;
}

void Partida::bajarGolosinas(){
	for (std::vector<Posicion>::iterator it = aEliminar.begin();
					it != aEliminar.end(); ++it) {
		bajarGolosinasDesde((*it).getX(),(*it).getY());
	}
}

void Partida::bajarGolosinasDesde(unsigned int x, unsigned int y) {
	for (unsigned int i = x; i > 0 && i <= x; i--) {
		CeldaBase * celdaActual = miTablero.getCelda(i, y);
		celdaActual->setGolosina(celdaActual->pedirGolosina(y));
	}
	//miTablero.getCelda(0, y)->llenarAleatoriamente();
	Pieza * p = JsonLevel::getPiezaProb(y);
	miTablero.getCelda(0, y)->setGolosina(p);
	//chequeo que no sea celda vacia
	if (miTablero.getCelda(0, y)->getGolosina() != NULL) {
		aRellenar.push_back(miTablero.getCelda(0, y)->getGolosina());
	}
}

//Se le pasa a esta funcion el numero mayor de la cant de matches
Pieza * getEspecial(int matches,Pieza::color c, MiniBar::orientacion o){
	if(matches == 3){
		MiniBar * mb = new MiniBar(c,o);
		return mb;
	}
	GoldenStar * gs = new GoldenStar();
	return gs;
}

void Partida::setearEspecial(unsigned int hMatches, unsigned int vMatches,
	unsigned int x, unsigned int y, Pieza::color c) {
	unsigned int max;
	MiniBar::orientacion o;
	//si son iguales, ganan los horizontales (arbitrario)
	if (hMatches >= vMatches) {
		max = hMatches;
		o = MiniBar::horizontal;
	} else {
		max = vMatches;
		o = MiniBar::vertical;
	}
	Pieza * p = getEspecial(max, c, o);
	miTablero.getCelda(x, y)->setGolosina(p);
}


bool Partida::movimientoValido(unsigned int x, unsigned int y) {
	unsigned int hMatches = getHorMatches(x,y);
	unsigned int vMatches = getVerMatches(x,y);
	//Verifico si en esa posicion tengo que poner una gs o mb
	if(hMatches > 2 || vMatches > 2){
		Pieza::color c = miTablero.getCelda(x, y)->getColorGolosina();
		if (c == Pieza::dorado){
			return false;
		}
		//elimino la celda que origina el match
		miTablero.getCelda(x, y)->eliminarContenido();
		posEspeciales.push_back(Posicion(x,y));
		setearEspecial(hMatches, vMatches, x, y, c);
		especiales.push_back(miTablero.getCelda(x, y)->getGolosina());
		return true;
	}
	if((hMatches > 1) || (vMatches > 1)){
		//como no hay que cambiarla a especial, lo agrego a los que
		//hay que eliminar
		aEliminar.push_back(Posicion(x,y));
		miTablero.getCelda(x, y)->getGolosina()->setPuntos(10);
		return true;
	}
	return false;
}


bool Partida::intercambiarGolosinas(Posicion p1, Posicion p2){
	//verifico que se puedan intercambiar las golosinas (no es celda vacia)
	//, y lo hago
	bool validarMovimientosPieza1, validarMovimientosPieza2, validarMovimientosEstrella;
	CeldaBase *celda1, *celda2;
	celda1 = miTablero.getCelda(p1.getX(),p1.getY());
	celda2 = miTablero.getCelda(p2.getX(),p2.getY());
	if(celda1->intercambiarGolosina(*celda2)){
		if (celda1->getColorGolosina() == Pieza::dorado || celda2->getColorGolosina() == Pieza::dorado){
			if (celda1->getColorGolosina() != Pieza::dorado){
				eliminarPiezasMismoColor(celda1->getColorGolosina());
				if(celda1->getGolosina()->getOrientacion() != Pieza::invalida){
					eliminarColumnaCompleta(p1.getX(), p1.getY(), &aEliminar);
					eliminarFilaCompleta(p1.getX(), p1.getY(), &aEliminar);
				}
				aEliminar.push_back(Posicion(p2.getX(),p2.getY()));
			}else if (celda2->getColorGolosina() != Pieza::dorado){
				eliminarPiezasMismoColor(celda2->getColorGolosina());
				if(celda2->getGolosina()->getOrientacion() != Pieza::invalida){
					eliminarColumnaCompleta(p2.getX(), p2.getY(), &aEliminar);
					eliminarFilaCompleta(p2.getX(), p2.getY(), &aEliminar);
				}
				aEliminar.push_back(Posicion(p1.getX(),p1.getY()));
			}else{
				eliminarPiezasMismoColor(Pieza::dorado);
			}
			sumarPuntos(aEliminar.size()*40);//40 puntos por golosina destruida
			validarMovimientosEstrella = true;
		}else{
			validarMovimientosEstrella = false;
			validarMovimientosPieza1 = movimientoValido(p1.getX(),p1.getY());
			validarMovimientosPieza2 = movimientoValido(p2.getX(),p2.getY());
		}
		if (validarMovimientosPieza1 || validarMovimientosPieza2 || validarMovimientosEstrella){
			Posicion::sortPosiciones(&aEliminar);
			Posicion::eliminarRepetidas(&aEliminar);
			eliminarCeldas();
			bajarGolosinas();
			return true;
		}else {
			//si no es valido, vuelvo a intercambiar las golosinas
			//para dejarlas como al ppio
			miTablero.getCelda(p1.getX(),p1.getY())->intercambiarGolosina(*miTablero.getCelda(p2.getX(),p2.getY()));
		}
	}
	//todo hay que sacar este limpiarVectores para ponerlo luego de eliminar las celdas
	limpiarVectores();
	return false;
}

//en caso de haber un match minimo de 3, se cambia la pieza de la
//posicion x, y porque es para inicializar el tablero
void Partida::validarTablero() {
	for (unsigned int y = 0; y < miTablero.getFilas(); y++) {
		for (unsigned int x = 0; x < miTablero.getColumnas(); x++) {
			bool der;
			if((x + 1) < this->getColumnas()){
				der = (miTablero.getCelda(x,y)->getColorGolosina() == miTablero.getCelda(x + 1,y)->getColorGolosina());
			} else {
				der = false;
			}
			bool abajo;
			if((y + 1) < this->getFilas()){
				abajo = (miTablero.getCelda(x,y)->getColorGolosina() == miTablero.getCelda(x,y + 1)->getColorGolosina());
			} else {
				abajo = false;
			}
			while(der || abajo){
				//Como hay 2 iguales seguidas, elimino la golosina
				//para poder reemplazarla, y asegurarme de que no haya
				//matches en el tablero original
				miTablero.getCelda(x,y)->eliminarContenido();
				miTablero.getCelda(x,y)->setGolosina(JsonLevel::getPiezaProb(y));
				if((x + 1) < this->getColumnas()){
					der = (miTablero.getCelda(x,y)->getColorGolosina() == miTablero.getCelda(x + 1,y)->getColorGolosina());
				} else {
					der = false;
				}
				if((y + 1) < this->getFilas()){
					abajo = (miTablero.getCelda(x,y)->getColorGolosina() == miTablero.getCelda(x,y + 1)->getColorGolosina());
				} else {
					abajo = false;
				}
			}
		}
	}
}

void Partida::completarTablero() {
	// celdas
	srand(time(0));
	int numCelda;
	bool vacia;
    for (Json::Value::iterator it = JsonLevel::getIterator("celdas");
		  it != JsonLevel::getFinIterator("celdas"); ++it) {
		vacia = (*it)["vacia"].asBool();
		numCelda = (*it)["numCelda"].asInt();
		if (vacia) {
			if (numCelda % miTablero.getFilas() == 0) {
				miTablero.addCelda(new CeldaVacia(NULL));

			} else {
				miTablero.addCelda(new CeldaVacia(miTablero.getCelda(numCelda-1)));
			}
		} else {
			Pieza * p = JsonLevel::getPiezaProb(it);
			if (numCelda % miTablero.getFilas() == 0){
				miTablero.addCelda(new Celda(p,NULL));
			} else {
				miTablero.addCelda(new Celda(p,miTablero.getCelda(numCelda -1)));
			}

		}
    }
}

void Partida::limpiarVectores(){
        aEliminar.clear();
        aRellenar.clear();
        especiales.clear();
        posEspeciales.clear();
}

unsigned int Partida::getFilas(){
        return miTablero.getFilas();
}

unsigned int Partida::getColumnas(){
        return miTablero.getColumnas();
}

std::vector<CeldaBase*>* Partida::getCeldas(){
        return miTablero.getCeldas();
}

CeldaBase* Partida::getCelda(unsigned int x, unsigned int y){
	return miTablero.getCelda(x,y);
}

void Partida::eliminarCeldas(){
	if (aEliminar.size() > 1){
		for (std::vector<Posicion>::iterator it = aEliminar.begin();
						it != aEliminar.end(); ++it) {
			sumarPuntos(miTablero.getCelda((*it).getX(),(*it).getY())->eliminarContenido());
		}
	}
}

std::vector<Pieza*> Partida::getARellenar(){
	return aRellenar;
}

void Partida::eliminarPiezasMismoColor(Pieza::color c){
	std::vector<CeldaBase*>* celdas = miTablero.getCeldas();
	int x,y, counter = 0;
	for (std::vector<CeldaBase*>::iterator it = celdas->begin(); it != celdas->end(); ++it){
		if (((*it)->getColorGolosina() == c) || (c == Pieza::dorado) && (!(*it)->isVacia())){
			x = counter%getFilas();
			y = counter/getFilas();
			aEliminar.push_back(Posicion(x,y));
		}
		counter++;
	}
}

std::vector<Posicion> Partida::getPosicionesEspeciales(){
	return posEspeciales;
}


std::vector<Pieza*> Partida::getEspeciales(){
	return especiales;
}

void Partida::eliminarFilaCompleta(unsigned int nFil, unsigned int nCol, std::vector<Posicion>* eliminar){
	for (unsigned int i = 0; i < getColumnas(); i++){
		if (i != nCol && !miTablero.getCelda(nFil, i)->isVacia()){
			eliminar->push_back(Posicion(nFil, i));
		}
	}
}

void Partida::eliminarColumnaCompleta(unsigned int nFil, unsigned int nCol, std::vector<Posicion>* eliminar){
	for (unsigned int i = 0; i < getFilas(); i++){
		if (i != nFil && !miTablero.getCelda(i, nCol)->isVacia()){
			eliminar->push_back(Posicion(i, nCol));
		}
	}
}

bool Partida::verificarMatchs(){
	std::vector<CeldaBase*>* celdas = miTablero.getCeldas();
	int x,y, counter = 0;
	bool algunMatch = false;
	for (std::vector<CeldaBase*>::iterator it = celdas->begin(); it != celdas->end(); ++it){
		x = counter%getFilas();
		y = counter/getFilas();
		if ((*it)->getGolosina() != NULL){
			algunMatch = algunMatch || movimientoValido(x, y);
		}
		counter++;
	}
	if (algunMatch){
		Posicion::sortPosiciones(&aEliminar);
		Posicion::eliminarRepetidas(&aEliminar);
		eliminarCeldas();
		bajarGolosinas();
	}
	return algunMatch;
}


int Partida::getPuntosJugada(){
	return puntosJugada;
}

void Partida::vaciarPuntosJugada(){
	puntosJugada = 0;
}

void Partida::sumarPuntos(int puntos){
	puntosJugada += (puntos*multiplicador);
}

void Partida::setMultiplicador(int mul){
	multiplicador = mul;
}

bool Partida::hayMatchesPosibles(){
	return miTablero.checkPossibleMatches();
}
